package com.jalarbee.core.money;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Currency;

import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.BigDecimalType;
import org.hibernate.type.CurrencyType;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;

public class MonetaryAmountType implements CompositeUserType {

	public String[] getPropertyNames() {
        // ORDER IS IMPORTANT!  it must match the order the columns are defined in the property mapping
        return new String[] { "currency", "amount" };
    }

    public Type[] getPropertyTypes() {
        return new Type[] { CurrencyType.INSTANCE, BigDecimalType.INSTANCE };
    }

    public Class<?> returnedClass() {
        return MonetaryAmount.class;
    }

    public Object getPropertyValue(Object component, int propertyIndex) {
        if ( component == null ) {
            return null;
        }

        final MonetaryAmount money = (MonetaryAmount) component;
        switch ( propertyIndex ) {
            case 0: {
                return money.getCurrency();
            }
            case 1: {
                return money.getAmount();
            }
            default: {
                throw new HibernateException( "Invalid property index [" + propertyIndex + "]" );
            }
        }
    }

    public void setPropertyValue(Object component, int propertyIndex, Object value) throws HibernateException {
        if ( component == null ) {
            return;
        }

        final MonetaryAmount money = (MonetaryAmount) component;
        switch ( propertyIndex ) {
            case 0: {
                money.setAmount( (BigDecimal) value );
                break;
            }
            case 1: {
                money.setCurrency( (Currency) value );
                break;
            }
            default: {
                throw new HibernateException( "Invalid property index [" + propertyIndex + "]" );
            }
        }
    }

    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLException {
        assert names.length == 2;
        BigDecimal amount = (BigDecimal) BigDecimalType.INSTANCE.get(rs, names[1], session ); // already handles null check
        Currency currency = (Currency) CurrencyType.INSTANCE.get( rs, names[0], session ); // already handles null check
        return amount == null && currency == null
                ? null
                : new MonetaryAmount(currency, amount );
    }

    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws SQLException {
        if ( value == null ) {
        	CurrencyType.INSTANCE.set( st, null, index );
        	BigDecimalType.INSTANCE.set( st, null, index + 1 );
        }
        else {
            final MonetaryAmount money = (MonetaryAmount) value;
            CurrencyType.INSTANCE.set( st, money.getCurrency(), index );
            BigDecimalType.INSTANCE.set( st, money.getAmount(), index + 1 );
        }
    }



    public int hashCode(Object x) throws HibernateException {
        return x.hashCode();
    }

    public boolean isMutable() {
        return false;
    }


    public Object replace(Object original, Object arg1, SessionImplementor session,
            Object target) throws HibernateException {
        return original;
    }


    public Object assemble(Serializable arg0, SessionImplementor arg1,
            Object arg2) throws HibernateException {
        return null;
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    public Serializable disassemble(Object value, SessionImplementor session)
            throws HibernateException {
        return (Serializable) value;
    }


    public boolean equals(Object x, Object y) throws HibernateException {
        if (x == y) {
            return true;
        } else if (x == null || y == null) {
            return false;
        }
        return x.equals(y);
    }


}
